﻿using Padsurface.Audio.Engine.Interfaces;
using System;
using System.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace Padsurface.Audio.Engine
{
    [Export(typeof(IMetronome))]
    public class Metronome : IMetronome
    {
        public event EventHandler<TimeSpan> OnTick;
        public bool CanStart { get { return _metronomeState.CanStart; } }

        private readonly TimeSpan _tickFrequency;
        private readonly object _lock;
        private IMetronomeState _metronomeState;

        internal Stopwatch Stopwatch { get; set; }
        internal TaskCompletionSource<bool> StartCompletionTask { get; set; }
        internal TaskCompletionSource<bool> StopCompletionTask { get; set; }
        internal CancellationTokenSource CancellationTokenSource { get; set; }

        public Metronome()
        {
            _tickFrequency = TimeSpan.FromMilliseconds(3);
            _lock = new object();

            SetState(new MetronomeStoppedState());
        }

        public Task<bool> Start()
        {
            lock (_lock)
            {
                return _metronomeState.Start(this);
            }
        }

        public Task<bool> Stop()
        {
            lock (_lock)
            {
                return _metronomeState.Stop(this);
            }
        }

        public void Reset()
        {
            lock (_lock)
            {
                _metronomeState.Reset(this);
            }
        }

        internal void SetState(IMetronomeState state)
        {
            if (state == null) throw new ArgumentNullException("state");

            _metronomeState = state;
        }

        internal void Tick()
        {
            Stopwatch = new Stopwatch();
            SetState(new MetronomeRunningState());
            StartCompletionTask.SetResult(true);

            Stopwatch.Start();

            while (!CancellationTokenSource.Token.IsCancellationRequested)
            {
                if(OnTick != null)
                    OnTick(this, Stopwatch.Elapsed);

                Task.Delay(_tickFrequency, CancellationTokenSource.Token);
            }

            Stopwatch.Stop();

            SetState(new MetronomeStoppedState());
            StopCompletionTask.SetResult(true);
        }
    }
}